In a typical Java implementation, whenever a Java application is executed, an instance of a Java virtual machine (JVM) is also started to support the execution of the Java application. If x Java applications are run concurrently, there would be x JVM instances. Typically, each JVM instance is an independent instance that is unaware of the other JVM's; thus, each JVM creates its own data structures and manages its own memory usage. There are many common data structures that are used by all JVM's. Because the various JVM instances are independent, they do not share these common data structures; rather, these data structures are duplicated by each JVM. The common data structures are usually stored in physical memory. That being the case, the physical memory will contain multiple copies of the common data structures (one copy per JVM), which is an inefficient use of the physical memory. In some implementations (e.g. personal digital assistants, cellular phones, etc.), physical memory is quite limited. In such implementations, this duplication of common data structures is highly undesirable.
The amount of common data structure duplication between multiple JVM instances can be significantly reduced by making “clones” of a master JVM. To do so, a master JVM is first instantiated. The master JVM is then initialized with all of the common data structures that will be shared by all of the JVM instances. These common data structures are stored in physical memory pages. Then, one or more “clones” of the master JVM are made (this may be done, for example, by using a “fork” instruction in Unix). Created in this way, each clone is still an independent JVM instance, but all of the clones will share the common data structures and reference the same physical memory pages. This is shown in FIG. 1, wherein the master JVM and the n number of JVM clones all reference and share the physical memory pages in which the common data structures are stored. Thus, the JVM clones do not maintain their own copies of the common data structures but rather share a common copy.
The sharing of the memory pages continues as long as none of the JVM's writes to one of the shared pages. If a JVM does write to one of the shared pages, then that JVM will no longer share that page with the other JVM's. For example, suppose that JVM clone n writes to and updates memory page m (see FIG. 2). When that happens, a copy of that page is made and the update is written into that page (updated page m) only. The original page m is not changed. Thereafter, JVM clone n will reference the updated page m, while the other JVM's will still reference the old page m. Notice that this update to page m does not affect the other pages. JVM clone n will still reference and share the other pages with the other JVM instances. Thus, a JVM instance will share a page with the other JVM instances unless and until it makes an update to that page.
In such a cloning implementation, it is desirable to share the memory pages as much as possible. This in turn means that the JVM clones should write to the shared pages as little as possible. The fewer the number of writes to the shared pages, the fewer the number of new pages that will be created and the more page sharing there will be. To maximize page sharing, it is desirable to be able to determine for each JVM clone how many new pages are created as a result of that JVM clone writing to the shared pages. This will reveal how much marginal memory is being consumed by the JVM clone (where marginal memory is the memory consumed by a JVM clone not including the shared memory pages). It is also desirable to be able to determine which portions of the JVM code are causing the writes to the shared pages. Given this information, it may be possible to change the code of the JVM to minimize and perhaps even eliminate writes to the shared pages. Currently, to obtain such information, many lines of instrumentation code would have to be added to the JVM code to detect and to record the writes to the shared pages. This instrumentation code would require a significant amount of effort to implement, and would significantly slow down the execution of the JVM. Thus, even though this approach is theoretically possible, it is not practically viable in many situations.